每個 Vue instance 都要經過一系列初始化步驟。當創建它時,從設置數據觀察(data observation)到編譯模板(compiling template),到將 instance 掛載(mount)到 DOM,最後到在數據更改期間更新 DOM。這個過程稱為 Vue instance 的生命週期,在創建到消滅 DOM 的過程中,Vue instance 中有默認運行一些 function。這些 Vue 組件以被創建和存在 Vue 的 instance 裡面,這些功能稱為生命週期鉤子(Hooks)。
圖上看到的白底紅框的方塊這些都是 Hooks,每個 Hooks 都是 function,我們可以透過這些 function 內設定一些我們要的行為,像是 AJAX 取資料的觸發時機點。
除了以上八個 Hooks 還有 activated hook
和 deactivated hook
可以使用,這些是在不同的情況下使用。
生命週期主要可以分 4 個階段以及額外的 1 個階段(activated 和 deactivated),我們先著重在 4 的部分,最後在補充說明 1 的內容。
Creation 階段是組件中運行的第一個 Hook 函式,與其他階段不同,它是在伺服器端渲染期間運行。
因此,若需要在客戶端渲染和伺服器渲染期間於元件當中設置事件,須於此階段進行設定。
什麼事都還沒做,只跟大家說我要開始囉!
這是最一開始的 Hook,在 vue instance 被初始化後同步呼叫。
需要知道的是,在此 Hook 時 data observation 和 event/watcher 等等都還沒建立。
我所有屬性都已經綁定囉,但是 $el 還沒建立,DOM 也還沒生成。
在此時期的 Hook 已經被初始化,在 vue instance 被建立後同步呼叫。
在此時期,包含 data observation, computed 屬性, methods, watch/event callbacks 等項目都已設定完成,但 mount 還沒開始所以 $el 屬性還沒建立好,也就是DOM 還沒生成可以讓我們操控。
接續上一個階段,首先他會先判斷是否有無 $el 屬性,若沒有則會嘗試另一種掛載方式(使用 $mount 掛載 el)。
再來會判斷是否有 $template 屬性 ,若有則直接使用 render function ,反之則使用 $el teamplate 進行模板編譯。
判斷完後進入正式進入 Mounting 階段。
我要開始產 virtual DOM 囉!
這是要開始產生 DOM 但尚未產生出 DOM 的時期(執行元素掛載之前),模板(template)和 scoped styles 都已經被編譯在這個時期,但你任然不能操控 DOM 元素,代表 $el 尚未被建立。
簡單來說就是:在這時期還沒有模板全部掛載到 HTML 的 DOM 上。
我已經努力的產生出 DOM 了!
元素已掛載, $el 被建立,這時就可以進行一些 HTML 的操作,這也是最常使用的生命週期 hook 。
假設有載入 Jqury 要到這個時期才能對 DOM 進行操作。
※ mounted 不保證此時所有的子 components 也都完成掛載,所以如果希望大家都掛載完成的話可以使用 vm.$nextTick 來替換 mounted,如以下所示
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
當元件的資料屬性發生變動,或其他因素導致畫面需要重新渲染時,則會調用 Updating 刷新網頁內容。
data 收到更新異動,我會在更新前調用它,但還不會渲染畫面!
這是 mounted hook
之後調用的生命週期 Hook,每當需要更新 DOM 的 data 時都會調用這個 Hook。
當資料改變被呼叫使用,還不會渲染 View,data 在變更的時候這個階段非常適合任何邏輯處理。
DOM已經完成更新啦!
當資料更新完成,則驅動 DOM 元素重新渲染畫面 View。
這裡可以執行 DOM 相關的操作,但是不建議在這個 Hook 中更改狀態,因為 Vue 已經為此提供了專門的方法來做這項事情(computed 和 watch)。
當呼叫 destroy 函式時,則會執行銷毀的動作,將原始的元件從 DOM 元素中移除。
※大多數的狀況不會調用到beforeDestroy和destroyed這兩個hook,最好使用v-if & v-for以data控制component的生命週期。
Vue instance被銷毀前調用,這時候大家功能都還能使用!
instance 和所有功能仍然完好無損,在這個時期都還能使用,您可以進行資源管理、刪除變量和清理組件。
Vue instance 所有的東西都會解除綁定,event listener也會被移除
這是 Vue 生命週期的最後一個時期,在這個時期,所有的DOM 元素綁定被解除、移除偵聽事件、Vue child 實例也被一併銷毀。
上面 4 個階段是我們常見的生命週期,那還有 1 個階段則是 activated hook
和 deactivated hook
,那它可以用來取代 Destruction 銷毀。
要避開 Destruction 階段並進入這個階段,需要搭配 HTML 的標籤 <keep-alive>
,其特性是可以維持資料狀態,只會有 activated hook 與 deactivated hook 的情況產生,則不觸發 destroyed hook 。
當我們需要保留狀態的時候用 <keep-alive>
這個標籤,像是頁籤切換時可以使用,並維持各頁籤的生命週期。當我們重新的進入某頁籤時週期的 Creation 和 Mounting 階段都不會執行,而是直接進到 deactivated 這個時期。
<keep-alive>
包裹動態組件時,會緩存不活動的組件實例,而不是銷毀它們。和<transition>
相似,<keep-alive>
是一個抽象組件:它自身不會渲染一個DOM元素,也不會出現在父組件鏈中。
Hooks:
<script>
const Child = {
template: '#childarea',
data: function() {
return {
text: 'Vue data 資料狀態'
}
},
beforeCreate() {
console.log(`beforeCreate! ${this.text}`);
},
created() {
alert(`created! ${this.text}`);
},
beforeMount() {
alert(`beforeMount! ${this.text}`);
},
mounted() {
alert(`mounted! ${this.text}`);
},
updated() {
console.log(`updated! ${this.text}`);
},
activated() {
alert(`activated! ${this.text}`);
},
deactivated() {
alert(`deactivated! ${this.text}`);
},
beforeDestroy() {
console.log(`beforeDestroy! ${this.text}`);
},
destroyed() {
console.log(`destroyed! ${this.text}`);
}
};
new Vue({
el: '#app',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
},
components: {
appChild: Child
}
});
</script>
<keep-alive>
在沒有 <keep-alive>
這個標籤的時候,生命週期走完按鈕隱藏 textbox,然後再按一次按鈕,vue 會產生全新的 textbox:
<div id="app" class="text-center">
<button @click="toggleShow" class="btn btn-primary">
<span v-if="isShowing">Hide child</span>
<span v-else>Show child</span>
</button>
<hr>
<app-child v-if="isShowing"></app-child>
</div>
<script type="text/x-template" id="childarea">
<div>
<h4>Hello! {{ text }}</h4>
<input type="text" class="form-control" v-model="text">
</div>
</script>
<keep-alive>
<div id="app" class="text-center">
<button @click="toggleShow" class="btn btn-primary">
<span v-if="isShowing">Hide child</span>
<span v-else>Show child</span>
</button>
<hr>
<keep-alive>
<app-child v-if="isShowing"></app-child>
</keep-alive>
</div>
<script type="text/x-template" id="childarea">
<div>
<h4>Hello! {{ text }}</h4>
<input type="text" class="form-control" v-model="text">
</div>
</script>